﻿using System;
using System.Linq;
using System.Globalization;
using Stimulus360.Models.Cms;
using Stimulus360.Models.Data;
using System.Collections.Generic;
using Stimulus360.Models.Data.CustomTypes;
using Stimulus360.Repositories.Interfaces;

namespace Stimulus360.Repositories
{
    /// <summary>
    ///     This class interacts with the Stimulus DB and the CMS DB to calculate
    ///     various data used for analytics
    /// </summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Does not hold expensive resources. Will be garbage collected automatically.")]
    public class AnalyticsRepository : IAnalyticsRepository
    {
        #region IAnalyticsRepository Members
        /// <summary>
        ///     The Linq to SQL DataContext to interact with the Stimulus Database.
        /// </summary>
        private readonly Stimulus360DataContext _dataContext = new Stimulus360DataContext();

        /// <summary>
        ///     The Linq to SQL DataContext to interact with the CMS Database.
        /// </summary>
        private readonly CmsDataContext _cmsDbContext = new CmsDataContext();

        private const string Q1 = "   Q1 ";
        private const string Q2 = "   Q2 ";
        private const string Q3 = "   Q3 ";
        private const string Q4 = "   Q4 ";

        /// <summary>
        ///     Method to get the Budget, Amount spent and jobs created for the top N 
        ///     Investment categories
        /// </summary>
        /// <param name="top">
        ///     Number of top categories to be considered
        /// </param>
        /// <returns>
        ///     The Budget, Amount spent and jobs created for the top N 
        ///     Investment categories
        /// </returns>
        public ICollection<BudgetSpendSummary> GetTopNBudgetSpendSummaries(int top)
        {
            return (from category in _dataContext.InvestmentCategories
                    orderby category.Score descending
                    select (new BudgetSpendSummary
                                {
                                    InvestmentCategory = category.Category,
                                    Budget = (int)category.FiscalYearBudget.GetValueOrDefault(0),
                                    Spent = (int)category.YtdSpend.GetValueOrDefault(0),
                                    JobCount = category.NewJobCount.GetValueOrDefault(0)
                                })).Take(top).ToList();

        }

        /// <summary>
        ///     Internal method to calculate the number of ideas from the given list
        ///     which were created in the given months
        /// </summary>
        /// <param name="quarterMonthList">
        ///     Months to be considered
        /// </param>
        /// <param name="ideaList">
        ///     The list of ideas to be considered
        /// </param>
        /// <returns>
        ///     Count of ideas created in the given quarter
        /// </returns>
        private static int QuarterWiseIdeaCount(ICollection<int> quarterMonthList, IEnumerable<Idea> ideaList)
        {
            return (from idea in ideaList
                    where quarterMonthList.Contains(idea.DateSubmitted.Month)
                    select idea).Count();
        }

        /// <summary>
        ///     Method to calculate number of projects completed in the given months
        /// </summary>
        /// <param name="quarterMonthList">
        ///     Set of months to consider
        /// </param>
        /// <param name="status">
        ///     The project status to consider.
        /// </param>
        /// <returns>
        ///     Number of projects completed in the given months
        /// </returns>
        private int QuarterWiseProjectCountForStatus(List<int> quarterMonthList, ProjectStatus status)
        {
          
                return (from proj in _dataContext.Projects
                        where (quarterMonthList.Contains(proj.ActualEndDate.GetValueOrDefault(new DateTime()).Month) && (proj.Status == (byte)status))
                        select proj).Count();
           
        }

        /// <summary>
        ///     Method to calculate the count of projects of the given status as of the given datetime
        /// </summary>
        /// <param name="time">
        ///     The Date time to be considered
        /// </param>
        /// <returns>
        ///     Count of projects of the given status as of the given datetime
        /// </returns>
        private int QuarterWiseActiveProjectCount(DateTime time)
        {
            return (from proj in _dataContext.Projects
                    where ((proj.DateCreated < time) && (proj.Status == (byte)ProjectStatus.InProgress))
                    select proj).Count();
        }

        /// <summary>
        ///     Count of ideas created during the current financial year
        /// </summary>
        /// <returns>
        ///     Quarter wise count of ideas created in the current financial year.
        /// </returns>
        public ICollection<IdeaTrend> GetIdeationTrend()
        {
            // We want ideas over last 5 months.

            int currentMonth = DateTime.Now.Month;
            int currentYear = DateTime.Now.Year;

            List<IdeaTrend> trendList = new List<IdeaTrend>();
            DateTime timeToConsider;
            if (currentMonth < CalendarYear.Jul)
            {
                int year = DateTime.Now.Year - 1;
                timeToConsider = new DateTime(year, CalendarYear.Jul, CalendarYear.FirstDay);

            }

            else
            {
                timeToConsider = new DateTime(currentYear, currentMonth, CalendarYear.FirstDay);


            }
            var dataToConsider = from idea in _dataContext.Ideas
                                 where idea.DateSubmitted >= timeToConsider
                                 select idea;
             List<Idea> ideaList   = dataToConsider.ToList();

            trendList.Add(new IdeaTrend { Quarter = Q1 + Environment.NewLine + CalendarYear.Q1, IdeaCount = QuarterWiseIdeaCount(CalendarYear.FirstQuarter, ideaList) });
            trendList.Add(new IdeaTrend { Quarter = Q2 + Environment.NewLine + CalendarYear.Q2, IdeaCount = QuarterWiseIdeaCount(CalendarYear.SecondQuarter, ideaList) });
            trendList.Add(new IdeaTrend { Quarter = Q3 + Environment.NewLine + CalendarYear.Q3, IdeaCount = QuarterWiseIdeaCount(CalendarYear.ThirdQuarter, ideaList) });
            trendList.Add(new IdeaTrend { Quarter = Q4 + Environment.NewLine + CalendarYear.Q4, IdeaCount = QuarterWiseIdeaCount(CalendarYear.FourthQuarter, ideaList) });

            return trendList;

        }

        /// <summary>
        ///     Count of projects Completed/Postponed/InProgress during the current financial year
        /// </summary>
        /// <returns>
        ///     Quarterwise count of projects Completed/Postponed/InProgress during the current financial year
        /// </returns>
        public ICollection<ProjectTrend> GetProjectTrend()
        {
            int currentMonth = DateTime.Now.Month;
            int currentYear = DateTime.Now.Year;
            int yearToConsider;

            List<ProjectTrend> trendList = new List<ProjectTrend>();
            if (currentMonth < CalendarYear.Jul)
            {
                int year = DateTime.Now.Year - 1;
                yearToConsider = year;
            }

            else
            {
                yearToConsider = currentYear;

            }

            trendList.Add(new ProjectTrend
            {
                Quarter = Q1 + Environment.NewLine + CalendarYear.Q1,
                InProgress = QuarterWiseActiveProjectCount(new DateTime(yearToConsider, CalendarYear.Sep, CalendarYear.ThirtyDays)),
                ProjectsCompleted = QuarterWiseProjectCountForStatus(CalendarYear.FirstQuarter.ToList(), ProjectStatus.Completed),
                StalledProjects = QuarterWiseProjectCountForStatus(CalendarYear.FirstQuarter.ToList(), ProjectStatus.Postponed)
            });
            trendList.Add(new ProjectTrend
            {
                Quarter = Q2 + Environment.NewLine + CalendarYear.Q2,
                InProgress = QuarterWiseActiveProjectCount(new DateTime(yearToConsider, CalendarYear.Dec, CalendarYear.ThirtyOneDays)),
                ProjectsCompleted = QuarterWiseProjectCountForStatus(CalendarYear.SecondQuarter.ToList(), ProjectStatus.Completed),
                StalledProjects = QuarterWiseProjectCountForStatus(CalendarYear.SecondQuarter.ToList(), ProjectStatus.Postponed)
            });
            trendList.Add(new ProjectTrend
           {
               Quarter = Q3 + Environment.NewLine + CalendarYear.Q3,
               InProgress = QuarterWiseActiveProjectCount(new DateTime(yearToConsider + 1, CalendarYear.Mar, CalendarYear.ThirtyOneDays)),
               ProjectsCompleted = QuarterWiseProjectCountForStatus(CalendarYear.ThirdQuarter.ToList(), ProjectStatus.Completed),
               StalledProjects = QuarterWiseProjectCountForStatus(CalendarYear.ThirdQuarter.ToList(), ProjectStatus.Postponed)
           });
            trendList.Add(new ProjectTrend
          {
              Quarter = Q4 + Environment.NewLine + CalendarYear.Q4,
              InProgress = QuarterWiseActiveProjectCount(new DateTime(yearToConsider + 1, CalendarYear.Jun, CalendarYear.ThirtyDays)),
              ProjectsCompleted = QuarterWiseProjectCountForStatus(CalendarYear.FourthQuarter.ToList(), ProjectStatus.Completed),
              StalledProjects = QuarterWiseProjectCountForStatus(CalendarYear.FourthQuarter.ToList(), ProjectStatus.Postponed)
          });

            return trendList;
        }

     


        /// <summary>
        ///     Method to get the sum of budgets allocated to all the Investment Categories for the 
        ///     current financial year
        /// </summary>
        /// <returns>
        ///     The sum of budgets allocated to all the Investment Categories for the 
        ///     current financial year
        /// </returns>
        public string GetStimulusBudget()
        {
            return (from category in _dataContext.InvestmentCategories
                    select category.FiscalYearBudget).Sum().GetValueOrDefault(0).ToString(CultureInfo.CurrentCulture).TrimEnd(new[] { '0' });
        }

        /// <summary>
        ///     Method to get the amount spent till Date by all the Investment Categories for the 
        ///     current financial year
        /// </summary>
        /// <returns>
        ///     The amount spent till Date by all the Investment Categories for the 
        ///     current financial year
        /// </returns>
        public string GetBudgetSpent()
        {
            return (from category in _dataContext.InvestmentCategories
                    select category.YtdSpend).Sum().GetValueOrDefault(0).ToString(CultureInfo.CurrentCulture).TrimEnd(new[] { '0' });
        }

        /// <summary>
        ///     Method to get the sum of jobs created across all investment categories for the current financial year
        /// </summary>
        /// <returns>
        ///     The sum of jobs created across all investment categories for the current financial year
        /// </returns>
        public string GetJobsCreated()
        {
            return (from category in _dataContext.InvestmentCategories
                    select category.NewJobCount).Sum().GetValueOrDefault(0).ToString(CultureInfo.CurrentCulture);
        }

        /// <summary>
        ///     Method infers and returns the set of active users in the system
        /// </summary>
        /// <returns>
        ///     Set of active users in the system
        /// </returns>
        public ICollection<string> GetActiveUsers()
        {
            DateTime viewDate = DateTime.Now.Subtract(new TimeSpan(CalendarYear.ThirtyDays , 0, 0, 0));
            var ideaRatingUsers = (from ideaRating in _dataContext.IdeaRatings
                                   where ((ideaRating.DateSubmitted >= viewDate) && ((ideaRating.Favorite == true) || ideaRating.Rating != 0))
                                   select ideaRating.UserName).Distinct();

            var projectRatingUsers = (from projRating in _dataContext.ProjectRatings
                                      where ((projRating.DateSubmitted >= viewDate) && ((projRating.Favorite == true) || projRating.Rating != 0))
                                      select projRating.UserName).Distinct();

            var ideaSubmitters = (from idea in _dataContext.Ideas
                                  where idea.DateSubmitted >= viewDate
                                  select idea.SubmittedBy).Distinct();

            var projectVolunteers = (from volunteer in _dataContext.Volunteers
                                     where volunteer.DateVolunteered >= viewDate
                                     select volunteer.UserName).Distinct();

            var photoSubmitters = (from photo in _cmsDbContext.ProjectPhotos
                                   where photo.DateUploaded >= viewDate
                                   select photo.SubmittedBy).Distinct();

            var docSubmitters = (from doc in _cmsDbContext.ProjectDocuments
                                 where doc.DateUploaded >= viewDate
                                 select doc.SubmittedBy).Distinct();
            var stimulusDbUsers = ideaRatingUsers.Union(projectRatingUsers).Union(ideaSubmitters).Union(projectVolunteers);
            var photoDocUsers = photoSubmitters.Union(docSubmitters);

            List<string> userNames = new List<string>();
            userNames.AddRange(stimulusDbUsers.ToList());
            userNames.AddRange(photoDocUsers.ToList());


            return (from name in userNames
                    select name.ToLower(CultureInfo.CurrentCulture)).Distinct().ToList();

        }

        /// <summary>
        ///     Method to get the number of completed projects in the system
        /// </summary>
        /// <returns>
        ///     Number of completed projects in the system
        /// </returns>
        public int GetProjectExecutedCount()
        {
            return (from project in _dataContext.Projects
                    where project.Status == (byte)ProjectStatus.Completed
                    select project).Count();
        }

        /// <summary>
        ///     Method returns total user count
        /// </summary>
        /// <returns>
        ///     The total user count
        /// </returns>
        public int GetAllUserCount()
        {
            return _dataContext.UserProfiles.Count();
        }
        #endregion
    }
}
